#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <cassert>

using namespace std;

// 父进程控制子进程

typedef void (*functor)();

vector<functor> functors; // 方法集合

// for debug
unordered_map<uint32_t, string> info;

void f1()
{
    cout << "这是一个处理日志的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}

void f2()
{
    cout << "这是一个备份数据任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}
void f3()
{
    cout << "这是一个处理网络连接的任务, 执行的进程 ID [" << getpid() << "]"
         << "执行时间是[" << time(nullptr) << "]\n"
         << endl;
}

void loadFunctor()
{
    info.insert({functors.size(), "处理日志的任务"});
    functors.push_back(f1);

    info.insert({functors.size(), "备份数据任务"});
    functors.push_back(f2);

    info.insert({functors.size(), "网络连接的任务"});
    functors.push_back(f3);
}

int main()
{
    // 0. 加载任务列表
    loadFunctor();

    // 1. 创建管道
    int pipefd[2] = {0};
    if (pipe(pipefd) != 0)
    {
        cerr << "pipe error" << endl;
        return 1;
    }

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        // 创建失败
        cerr << "fork error" << endl;
        return 2;
    }
    else if (id == 0)
    {
        // 子进程，read - 读取
        // 3. 关闭不需要的文件fd
        close(pipefd[1]);

        // 子进程不断根据收到的信息，执行对应的方法
        // 如果没有人往管道中写，此时子进程就卡在了read这里等待别人分配任务
        while (true)
        {
            uint32_t operatorType = 0;

            // 从fd为pipefd[0]的文件里读sizeof(uint32_t)个字节的内容，写到operatorType中去

            // 如果有数据就读取，如果没有数据就阻塞等待，等待任务的到来。
            ssize_t s = read(pipefd[0], &operatorType, sizeof(uint32_t));
            if (s == 0)
            {
                cout << "我要退出了..." << endl;
                break;
            }
            assert(s == sizeof(uint32_t));
            (void)s;
            // assert是断言，编译有效，debug 模式。
            // release 模式，断言就没了。
            // 一旦断言没有了，s变量就是只被定义了，没有被使用。
            // release模式中，可能会有warning。

            // 走到这里一定是一个成功的读取
            if (operatorType < functors.size())
            {
                functors[operatorType]();
            }
            else
            {
                cerr << "bug? operatorType = " << operatorType << endl;
            }
        }
        close(pipefd[0]);
        exit(0);
    }
    else if (id > 0)
    {
        srand((long long)time(nullptr));
        // 父进程，write - 操作
        // 3. 关闭不需要的文件fd
        close(pipefd[0]);
        // 4. 指派任务
        int num = functors.size();
        int cnt = 5;
        while (cnt--)
        {
            // 5. 形成任务码
            uint32_t commandCode = rand() % num;
            cout << "父进程指派任务完成，任务是：" << info[commandCode] << "任务的编号是: " << cnt << endl;
            
            // 向指定的进程下达执行任务的操作
            write(pipefd[1], &commandCode, sizeof(uint32_t));
            sleep(1);
        }
        close(pipefd[1]);
        
        pid_t res = waitpid(id, nullptr, 0);
        if (res) cout << "wait success" << endl;
    }

    return 0;
}
